#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <functional>
#include "log.hpp"

Log lg;

// 自定义函数指针的两种形式
// 1.
using func_t = std::function<std::string(const std::string &)>;
// 2.
// typedef std::function<std::string(const std::string&)> func_t;

// #define SIZE 1024
uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";
const int size=1024;
enum
{
    SOCKET_ERR = 1,
    BIND_ERR
};

class UdpServer
{
public:
    UdpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip)
        :sockfd(0), _ip(ip), _port(port), isrunning(false)
    {
    }

    void Init() // 创建+绑定
    {
        // 创建Udp socket
        // AF_INET:IPv4协议族  SOCK_DGRAM：UDP数据报模式  第三个：由系统自动匹配UDP协议
        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd < 0)
        {
            lg(Fatal, "socket creat error,sockfd: %d", sockfd);
            exit(SOCKET_ERR);
        }
        // 绑定地址
        // 用结构体描述服务器地址（IP+端口）
        struct sockaddr_in local;
        bzero(&local, sizeof(local));  // 清空结构体
        local.sin_family = AF_INET;    // IPv4协议
        local.sin_port = htons(_port); // 端口转网络字节序（大端）
        // 将字符串IP转为网络字节序的二进制地址--这里封装了两层地址
        // local.sin_addr.s_addr = inet_addr(_ip.c_str());
        local.sin_addr.s_addr=INADDR_ANY;
        // 绑定-将创建的sockfd,与IP+端口绑定，让系统知道该Socket处理哪个地址的UDP数据
        int s = bind(sockfd, (const struct sockaddr *)&local, sizeof(local));
        if (s < 0)
        {
            lg(Fatal, "bind error,errno:%d,err string:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        lg(Info, "bind success,errno:%d,err string:%s", errno, strerror(errno));
    }

    // UDP服务器核心逻辑，用于处理持续接收2客户端数据，处理并回发
    void Run(func_t func)
    {
        std::cout << "hello message" << std::endl;

        isrunning = true;
        char buffer[size];
        while (isrunning)
        {
            // 存客户端的地址（IP+端口）
            struct sockaddr_in client;

            // 阻塞接收UDP数据，同时获取客户端地址
            // recvfrom
            // 留一字节存'\0'避免溢出
            socklen_t len = sizeof(client);
            std::cout << "hello recvfrom before" << std::endl;

            ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&client, &len);
            std::cout << "hello recvfrom after" << std::endl;
            
            if (n < 0)
            {
                lg(Warning, "recvfrom error,errno:%d,err string:%s", errno, strerror(errno));
                continue;
            }
            buffer[n] = 0; // 手动补'\0'，让缓冲区成为合法的C字符串
            std::cout << buffer << std::endl;
            std::string info = buffer;
            // std::cout<<info<<std::endl;
            std::string echo_string = func(info); // 调用业务逻辑处理数据
            // sendto(sockfd, buffer, sizeof(buffer), 0, (const struct sockaddr *)&client, len);
            // std::string echo_string = "echo-"+info; // 调用业务逻辑处理数据

            sendto(sockfd, echo_string.c_str(), echo_string.size(), 0, (const struct sockaddr *)&client, len);
        }
    }
    ~UdpServer()
    {
        if (sockfd > 0)
            close(sockfd);
    }

private:
    int sockfd;      // 网络文件描述符
    std::string _ip; // 任意地址bind
    uint16_t _port;  // 表面服务器的进程的的端口
    bool isrunning;  // 判断是否运行
};